<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>1. Vue基础 | Blog</title>
    <meta name="generator" content="VuePress 1.9.10">
    <link rel="icon" href="/blog/img/logo.png">
    <link rel="manifest" href="/blog/manifest.json">
    <link rel="apple-touch-icon" href="/blog/icons/apple-touch-icon-152x152.png">
    <link rel="mask-icon" href="/blog/icons/safari-pinned-tab.svg" color="#3eaf7c">
    <meta name="description" content="blog | js | node | vue | vuepress | webpack">
    <meta name="theme-color" content="#3eaf7c">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="msapplication-TileImage" content="/icons/msapplication-icon-144x144.png">
    <meta name="msapplication-TileColor" content="#000000">
    
    <link rel="preload" href="/blog/assets/css/0.styles.b6b82f87.css" as="style"><link rel="preload" href="/blog/assets/js/app.43c5da00.js" as="script"><link rel="preload" href="/blog/assets/js/2.d3a12b6d.js" as="script"><link rel="preload" href="/blog/assets/js/1.f8bb34da.js" as="script"><link rel="preload" href="/blog/assets/js/40.dfcfa312.js" as="script"><link rel="prefetch" href="/blog/assets/js/10.fde088a2.js"><link rel="prefetch" href="/blog/assets/js/11.c62b6b34.js"><link rel="prefetch" href="/blog/assets/js/12.27d4f152.js"><link rel="prefetch" href="/blog/assets/js/13.419d7748.js"><link rel="prefetch" href="/blog/assets/js/14.eb7a3d07.js"><link rel="prefetch" href="/blog/assets/js/15.114dfd5c.js"><link rel="prefetch" href="/blog/assets/js/16.85253907.js"><link rel="prefetch" href="/blog/assets/js/17.c2838453.js"><link rel="prefetch" href="/blog/assets/js/18.3256f17f.js"><link rel="prefetch" href="/blog/assets/js/19.d8afd0ae.js"><link rel="prefetch" href="/blog/assets/js/20.0d880388.js"><link rel="prefetch" href="/blog/assets/js/21.33b300c9.js"><link rel="prefetch" href="/blog/assets/js/22.4137860d.js"><link rel="prefetch" href="/blog/assets/js/23.b74cc977.js"><link rel="prefetch" href="/blog/assets/js/24.d99ad412.js"><link rel="prefetch" href="/blog/assets/js/25.ca9a652b.js"><link rel="prefetch" href="/blog/assets/js/26.66d6bac4.js"><link rel="prefetch" href="/blog/assets/js/27.95990a8e.js"><link rel="prefetch" href="/blog/assets/js/28.761c6ea4.js"><link rel="prefetch" href="/blog/assets/js/29.254ffe48.js"><link rel="prefetch" href="/blog/assets/js/3.af33e5d6.js"><link rel="prefetch" href="/blog/assets/js/30.6e4a155b.js"><link rel="prefetch" href="/blog/assets/js/31.665d02a7.js"><link rel="prefetch" href="/blog/assets/js/32.45b461b5.js"><link rel="prefetch" href="/blog/assets/js/33.17a0771a.js"><link rel="prefetch" href="/blog/assets/js/34.af9f8240.js"><link rel="prefetch" href="/blog/assets/js/35.e9dd53af.js"><link rel="prefetch" href="/blog/assets/js/36.e900b84a.js"><link rel="prefetch" href="/blog/assets/js/37.52783c0e.js"><link rel="prefetch" href="/blog/assets/js/38.5cb7cb7f.js"><link rel="prefetch" href="/blog/assets/js/39.0d72378d.js"><link rel="prefetch" href="/blog/assets/js/4.0a92875f.js"><link rel="prefetch" href="/blog/assets/js/41.3ae8c9d7.js"><link rel="prefetch" href="/blog/assets/js/42.bed07f3e.js"><link rel="prefetch" href="/blog/assets/js/43.170da95d.js"><link rel="prefetch" href="/blog/assets/js/44.eec82bc0.js"><link rel="prefetch" href="/blog/assets/js/45.b5a61f3a.js"><link rel="prefetch" href="/blog/assets/js/46.dbdb669f.js"><link rel="prefetch" href="/blog/assets/js/47.f3b25bee.js"><link rel="prefetch" href="/blog/assets/js/48.6c3ba867.js"><link rel="prefetch" href="/blog/assets/js/49.2c3070ea.js"><link rel="prefetch" href="/blog/assets/js/5.3b9be48d.js"><link rel="prefetch" href="/blog/assets/js/50.4a2a7438.js"><link rel="prefetch" href="/blog/assets/js/51.b3b493a3.js"><link rel="prefetch" href="/blog/assets/js/52.a56b61d7.js"><link rel="prefetch" href="/blog/assets/js/53.8f36ae1d.js"><link rel="prefetch" href="/blog/assets/js/54.8ee09618.js"><link rel="prefetch" href="/blog/assets/js/55.527c5d9c.js"><link rel="prefetch" href="/blog/assets/js/56.a09690f4.js"><link rel="prefetch" href="/blog/assets/js/57.cf4bc588.js"><link rel="prefetch" href="/blog/assets/js/58.0c8b9d19.js"><link rel="prefetch" href="/blog/assets/js/59.7d09c3f1.js"><link rel="prefetch" href="/blog/assets/js/6.7b96ebf4.js"><link rel="prefetch" href="/blog/assets/js/60.52e5c6db.js"><link rel="prefetch" href="/blog/assets/js/61.afa66052.js"><link rel="prefetch" href="/blog/assets/js/62.d7a75678.js"><link rel="prefetch" href="/blog/assets/js/63.77765300.js"><link rel="prefetch" href="/blog/assets/js/64.9777d386.js"><link rel="prefetch" href="/blog/assets/js/65.deeca199.js"><link rel="prefetch" href="/blog/assets/js/66.aba723e9.js"><link rel="prefetch" href="/blog/assets/js/67.47b5088b.js"><link rel="prefetch" href="/blog/assets/js/68.3178dffd.js"><link rel="prefetch" href="/blog/assets/js/69.a0f4810a.js"><link rel="prefetch" href="/blog/assets/js/7.7e58b9c4.js"><link rel="prefetch" href="/blog/assets/js/70.5b120c62.js"><link rel="prefetch" href="/blog/assets/js/71.2335838f.js"><link rel="prefetch" href="/blog/assets/js/vendors~docsearch.2493936b.js">
    <link rel="stylesheet" href="/blog/assets/css/0.styles.b6b82f87.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/blog/" class="home-link router-link-active"><!----> <span class="site-name">Blog</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/blog/" class="nav-link">
  home
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="long-ui" class="dropdown-title"><span class="title">long-ui</span> <span class="arrow down"></span></button> <button type="button" aria-label="long-ui" class="mobile-dropdown-title"><span class="title">long-ui</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/ui/" class="nav-link">
  UI简介
</a></li><li class="dropdown-item"><!----> <a href="https://weberlong.github.io/long-ui/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  预览
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://github.com/WeberLong/long-ui" target="_blank" rel="noopener noreferrer" class="nav-link external">
  源码
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="/blog/archives/" class="nav-link">
  archives
</a></div><div class="nav-item"><a href="/blog/interview/" class="nav-link router-link-active">
  interview
</a></div> <a href="https://github.com/WeberLong" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/blog/" class="nav-link">
  home
</a></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="long-ui" class="dropdown-title"><span class="title">long-ui</span> <span class="arrow down"></span></button> <button type="button" aria-label="long-ui" class="mobile-dropdown-title"><span class="title">long-ui</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/blog/ui/" class="nav-link">
  UI简介
</a></li><li class="dropdown-item"><!----> <a href="https://weberlong.github.io/long-ui/" target="_blank" rel="noopener noreferrer" class="nav-link external">
  预览
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li><li class="dropdown-item"><!----> <a href="https://github.com/WeberLong/long-ui" target="_blank" rel="noopener noreferrer" class="nav-link external">
  源码
  <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></li></ul></div></div><div class="nav-item"><a href="/blog/archives/" class="nav-link">
  archives
</a></div><div class="nav-item"><a href="/blog/interview/" class="nav-link router-link-active">
  interview
</a></div> <a href="https://github.com/WeberLong" target="_blank" rel="noopener noreferrer" class="repo-link">
    GitHub
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><a href="/blog/interview/" aria-current="page" class="sidebar-link">面试题</a></li><li><section class="sidebar-group depth-0"><a href="/blog/interview/css/1/" class="sidebar-heading clickable"><span>css</span> <!----></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/blog/interview/css/1.html" class="sidebar-link">1. 如何垂直居中一个元素？</a></li><li><a href="/blog/interview/css/2.html" class="sidebar-link">2. BFC</a></li><li><a href="/blog/interview/css/3.html" class="sidebar-link">3. 移动端1px问题的解决办法</a></li><li><a href="/blog/interview/css/4.html" class="sidebar-link">4. CSS清除浮动的几种方法</a></li><li><a href="/blog/interview/css/5.html" class="sidebar-link">5. opacity:0、visibility:hidden 和 display:none 三者区别？</a></li><li><a href="/blog/interview/css/6.html" class="sidebar-link">6. css绘制三角形五种方法？</a></li></ul></section></li><li><section class="sidebar-group depth-0"><a href="/blog/interview/js/1/" class="sidebar-heading clickable"><span>js</span> <!----></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/blog/interview/js/1.html" class="sidebar-link">1. 谈谈对this的理解</a></li><li><a href="/blog/interview/js/2.html" class="sidebar-link">2. new操作符具体干了什么呢？如何实现？</a></li><li><a href="/blog/interview/js/3.html" class="sidebar-link">3. 什么是原型链？</a></li><li><a href="/blog/interview/js/4.html" class="sidebar-link">4. 事件委托是什么？</a></li><li><a href="/blog/interview/js/5.html" class="sidebar-link">5. 重排与重绘的区别，什么情况下会触发？</a></li><li><a href="/blog/interview/js/6.html" class="sidebar-link">6. 去除数组重复成员的方法</a></li><li><a href="/blog/interview/js/7.html" class="sidebar-link">7. 深拷贝和浅拷贝的区别是什么？实现一个深拷贝</a></li><li><a href="/blog/interview/js/8.html" class="sidebar-link">8. 柯里化函数实现</a></li><li><a href="/blog/interview/js/9.html" class="sidebar-link">9. 什么是闭包？闭包的作用是什么？</a></li><li><a href="/blog/interview/js/10.html" class="sidebar-link">10. Promise的 all/any/race/allSettled 方法的区别</a></li><li><a href="/blog/interview/js/11.html" class="sidebar-link">11. jsonp的原理是什么？</a></li><li><a href="/blog/interview/js/12.html" class="sidebar-link">12. 介绍一下js的节流与防抖?</a></li><li><a href="/blog/interview/js/13.html" class="sidebar-link">13. js的事件循环是什么？</a></li><li><a href="/blog/interview/js/14.html" class="sidebar-link">14. 手写call、apply及bind函数</a></li><li><a href="/blog/interview/js/15.html" class="sidebar-link">15. ['1', '2', '3'].map(parseInt)执行结果，what &amp; why ?</a></li><li><a href="/blog/interview/js/16.html" class="sidebar-link">16. 手写一个Promise</a></li><li><a href="/blog/interview/js/17.html" class="sidebar-link">17. js数组扁平化的六种方式</a></li><li><a href="/blog/interview/js/18.html" class="sidebar-link">18. webpack相关问题</a></li><li><a href="/blog/interview/js/19.html" class="sidebar-link">19. http和https的区别</a></li><li><a href="/blog/interview/js/20.html" class="sidebar-link">20. 实现 instanceOf</a></li><li><a href="/blog/interview/js/21.html" class="sidebar-link">21. 打印出1-10000之间的所有对称数</a></li><li><a href="/blog/interview/js/22.html" class="sidebar-link">22. for of, for in和forEach, map的区别？</a></li></ul></section></li><li><section class="sidebar-group depth-0"><a href="/blog/interview/frame/1/" class="sidebar-heading clickable open"><span>框架</span> <!----></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/blog/interview/frame/1.html" aria-current="page" class="active sidebar-link">1. Vue基础</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_1-computed-和-watch-的区别" class="sidebar-link">1. Computed 和 Watch 的区别</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_2-常见的事件修饰符及其作用" class="sidebar-link">2. 常见的事件修饰符及其作用</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_3-v-if和v-show的区别" class="sidebar-link">3. v-if和v-show的区别</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_4-data-为什么是一个函数而不是对象" class="sidebar-link">4. data 为什么是一个函数而不是对象</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_5-vue中封装的数组方法有哪些-其如何实现页面更新" class="sidebar-link">5. Vue中封装的数组方法有哪些，其如何实现页面更新</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_6-谈谈mvvm" class="sidebar-link">6. 谈谈MVVM</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_7-使用-object-defineproperty-来进行数据劫持有什么缺点" class="sidebar-link">7. 使用 Object.defineProperty() 来进行数据劫持有什么缺点？</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_8-说一下vue的生命周期" class="sidebar-link">8. 说一下Vue的生命周期</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_9-vue-子组件和父组件执行顺序" class="sidebar-link">9. Vue 子组件和父组件执行顺序</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_10-一般在哪个生命周期请求异步数据" class="sidebar-link">10. 一般在哪个生命周期请求异步数据</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_11-vue-组件通信方式" class="sidebar-link">11. Vue 组件通信方式</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_12-vue-路由模式有哪些-有什么区别" class="sidebar-link">12. Vue 路由模式有哪些？有什么区别？</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_13-说说-vue-router-的-hash模式" class="sidebar-link">13. 说说 Vue Router 的 hash模式</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_14-route-和-router-的区别" class="sidebar-link">14. `$route` 和`$router` 的区别</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_15-vuex-是什么-核心流程" class="sidebar-link">15. Vuex 是什么？核心流程？</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_16-vuex中action和mutation的区别" class="sidebar-link">16. Vuex中action和mutation的区别</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_17-redux-和-vuex-有什么区别-它们的共同思想" class="sidebar-link">17. Redux 和 Vuex 有什么区别，它们的共同思想</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_18-vuex-和-localstorage-的区别" class="sidebar-link">18. Vuex 和 localStorage 的区别</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_19-vue3-0有什么更新" class="sidebar-link">19. Vue3.0有什么更新</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_20-defineproperty和proxy的区别" class="sidebar-link">20. defineProperty和proxy的区别</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_21-对虚拟dom的理解" class="sidebar-link">21. 对虚拟DOM的理解？</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_22-虚拟dom的解析过程" class="sidebar-link">22. 虚拟DOM的解析过程</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_23-双向数据绑定的原理" class="sidebar-link">23. 双向数据绑定的原理</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_24-diff-算法" class="sidebar-link">24. Diff 算法</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_25-vue-为什么要设置-key" class="sidebar-link">25. Vue 为什么要设置 key？</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_26-为什么不建议用index作为key" class="sidebar-link">26. 为什么不建议用index作为key?</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_27-编译的过程" class="sidebar-link">27. 编译的过程</a></li><li class="sidebar-sub-header"><a href="/blog/interview/frame/1.html#_28-nexttick-原理分析" class="sidebar-link">28. NextTick 原理分析</a></li></ul></li><li><a href="/blog/interview/frame/2.html" class="sidebar-link">2. 30道vue面试题</a></li></ul></section></li><li><section class="sidebar-group depth-0"><a href="/blog/interview/leetcode/1/" class="sidebar-heading clickable"><span>算法</span> <!----></a> <ul class="sidebar-links sidebar-group-items"><li><a href="/blog/interview/leetcode/1.html" class="sidebar-link">1. JS面试中常见的算法题</a></li><li><a href="/blog/interview/leetcode/2.html" class="sidebar-link">2. 说说你对二分查找的理解？如何实现？</a></li><li><a href="/blog/interview/leetcode/3.html" class="sidebar-link">3. 介绍下深度优先遍历和广度优先遍历，如何实现？</a></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="_1-vue基础"><a href="#_1-vue基础" class="header-anchor">#</a> 1. Vue基础</h1> <h2 id="_1-computed-和-watch-的区别"><a href="#_1-computed-和-watch-的区别" class="header-anchor">#</a> <strong>1. Computed 和 Watch 的区别</strong></h2> <ul><li>computed 计算属性 : 依赖其它属性值，并且 computed 的值有缓存，只有它依赖的属性值发生改变，下一次获取 computed 的值时才会重新计算 computed 的值。</li> <li>watch 侦听器 : 更多的是观察的作用，无缓存性，类似于某些数据的监听回调，每当监听的数据变化时都会执行回调进行后续操作。</li> <li>运用场景
<ul><li>当需要进行数值计算,并且依赖于其它数据时，应该使用 computed，因为可以利用 computed 的缓存特性，避免每次获取值时都要重新计算。</li> <li>当需要在数据变化时执行异步或开销较大的操作时，应该使用 watch，使用 watch 选项允许执行异步操作 ( 访问一个 API )，限制执行该操作的频率，并在得到最终结果前，设置中间状态。这些都是计算属性无法做到的。</li></ul></li></ul> <h2 id="_2-常见的事件修饰符及其作用"><a href="#_2-常见的事件修饰符及其作用" class="header-anchor">#</a> <strong>2. 常见的事件修饰符及其作用</strong></h2> <ul><li>.stop：等同于 JavaScript 中的 event.stopPropagation() ，防止事件冒泡；</li> <li>.prevent：等同于 JavaScript 中的 event.preventDefault() ，防止执行预设的行为（如果事件可取消，则取消该事件，而不停止事件的进一步传播）；</li> <li>.capture：与事件冒泡的方向相反，事件捕获由外到内；</li> <li>.self：只会触发自己范围内的事件，不包含子元素；</li> <li>.once：只会触发一次</li></ul> <h2 id="_3-v-if和v-show的区别"><a href="#_3-v-if和v-show的区别" class="header-anchor">#</a> <strong>3. v-if和v-show的区别</strong></h2> <ul><li>手段：v-if是动态的向DOM树内添加或者删除DOM元素；v-show是通过设置DOM元素的display样式属性控制显隐；</li> <li>编译过程：v-if切换有一个局部编译/卸载的过程，切换过程中合适地销毁和重建内部的事件监听和子组件；v-show只是简单的基于css切换；</li> <li>编译条件：v-if是惰性的，如果初始条件为假，则什么也不做；只有在条件第一次变为真时才开始局部编译; v-show是在任何条件下，无论首次条件是否为真，都被编译，然后被缓存，而且DOM元素保留；</li> <li>性能消耗：v-if有更高的切换消耗；v-show有更高的初始渲染消耗；</li> <li>使用场景：v-if适合运营条件不大可能改变；v-show适合频繁切换。</li></ul> <h2 id="_4-data-为什么是一个函数而不是对象"><a href="#_4-data-为什么是一个函数而不是对象" class="header-anchor">#</a> <strong>4. data 为什么是一个函数而不是对象</strong></h2> <ul><li>JavaScript中的对象是引用类型的数据，当多个实例引用同一个对象时，只要一个实例对这个对象进行操作，其他实例中的数据也会发生变化。</li> <li>而在 Vue 中，更多的是想要复用组件，那就需要每个组件都有自己的数据，这样组件之间才不会相互干扰。</li> <li>所以组件的数据不能写成对象的形式，而是要写成函数的形式。数据以函数返回值的形式定义，这样当每次复用组件的时候，就会返回一个新的data，也就是说每个组件都有自己的私有数据空间，它们各自维护自己的数据，不会干扰其他组件的正常运行。</li></ul> <h2 id="_5-vue中封装的数组方法有哪些-其如何实现页面更新"><a href="#_5-vue中封装的数组方法有哪些-其如何实现页面更新" class="header-anchor">#</a> <strong>5. Vue中封装的数组方法有哪些，其如何实现页面更新</strong></h2> <ul><li>&quot;push&quot;,&quot;pop&quot;,  &quot;shift&quot;,  &quot;unshift&quot;,  &quot;splice&quot;,  &quot;sort&quot;,  &quot;reverse&quot;</li> <li>在Vue中，对响应式处理利用的是Object.defineProperty对数据进行拦截，而这个方法并不能监听到数组内部变化，数组长度变化，数组的截取变化等，所以需要对这些操作进行hack，让Vue能监听到其中的变化。</li></ul> <h2 id="_6-谈谈mvvm"><a href="#_6-谈谈mvvm" class="header-anchor">#</a> <strong>6. 谈谈MVVM</strong></h2> <ul><li>MVVM 分为 Model、View、ViewModel：</li> <li>Model代表数据模型，数据和业务逻辑都在Model层中定义；</li> <li>View代表UI视图，负责数据的展示；</li> <li>ViewModel负责监听Model中数据的改变并且控制视图的更新，处理用户交互操作；</li> <li>Model和View并无直接关联，而是通过ViewModel来进行联系的，Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新，View中由于用户交互操作而改变的数据也会在Model中同步。</li> <li>这种模式实现了 Model和View的数据自动同步，因此开发者只需要专注于数据的维护操作即可，而不需要自己操作DOM。</li></ul> <h2 id="_7-使用-object-defineproperty-来进行数据劫持有什么缺点"><a href="#_7-使用-object-defineproperty-来进行数据劫持有什么缺点" class="header-anchor">#</a> <strong>7. 使用 Object.defineProperty() 来进行数据劫持有什么缺点？</strong></h2> <ul><li>在对一些属性进行操作时，使用这种方法无法拦截，比如通过下标方式修改数组数据或者给对象新增属性，这都不能触发组件的重新渲染，因为 Object.defineProperty 不能拦截到这些操作。更精确的来说，对于数组而言，大部分操作都是拦截不到的，只是 Vue 内部通过重写函数的方式解决了这个问题。</li> <li>在 Vue3.0 中已经不使用这种方式了，而是通过使用 Proxy 对对象进行代理，从而实现数据劫持。使用Proxy 的好处是它可以完美的监听到任何方式的数据改变，唯一的缺点是兼容性的问题，因为 Proxy 是 ES6 的语法。</li></ul> <h2 id="_8-说一下vue的生命周期"><a href="#_8-说一下vue的生命周期" class="header-anchor">#</a> <strong>8. 说一下Vue的生命周期</strong></h2> <ul><li>概念：Vue 实例有⼀个完整的⽣命周期，也就是从开始创建、初始化数据、编译模版、挂载Dom -&gt; 渲染、更新 -&gt; 渲染、卸载 等⼀系列过程，称这是Vue的⽣命周期。</li> <li>beforeCreate（创建前）：数据观测和初始化事件还未开始，此时 data 的响应式追踪、event/watcher 都还没有被设置，也就是说不能访问到data、computed、watch、methods上的方法和数据。</li> <li>created（创建后） ：实例创建完成，实例上配置的 options 包括 data、computed、watch、methods 等都配置完成，但是此时渲染得节点还未挂载到 DOM，所以不能访问到 <code>$el</code> 属性。</li> <li>beforeMount（挂载前）：在挂载开始之前被调用，相关的render函数首次被调用。实例已完成以下的配置：编译模板，把data里面的数据和模板生成html。此时还没有挂载html到页面上。</li> <li>mounted（挂载后）：在el被新创建的 <code>vm.$el</code> 替换，并挂载到实例上去之后调用。实例已完成以下的配置：用上面编译好的html内容替换el属性指向的DOM对象。完成模板中的html渲染到html 页面中。</li> <li>beforeUpdate（更新前）：响应式数据更新时调用，此时虽然响应式数据更新了，但是对应的真实 DOM 还没有被渲染。</li> <li>updated（更新后） ：在由于数据更改导致的虚拟DOM重新渲染和打补丁之后调用。此时 DOM 已经根据响应式数据的变化更新了。调用时，组件 DOM已经更新，所以可以执行依赖于DOM的操作。然而在大多数情况下，应该避免在此期间更改状态，因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用。</li> <li>beforeDestroy（销毁前）：实例销毁之前调用。这一步，实例仍然完全可用，this 仍能获取到实例。</li> <li>destroyed（销毁后）：实例销毁后调用，调用后，Vue 实例指示的所有东西都会解绑定，所有的事件监听器会被移除，所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。</li> <li>另外还有 keep-alive 独有的生命周期，分别为 activated 和 deactivated 。用 keep-alive 包裹的组件在切换时不会进行销毁，而是缓存到内存中并执行 deactivated 钩子函数，命中缓存渲染后会执行 activated 钩子函数。</li></ul> <h2 id="_9-vue-子组件和父组件执行顺序"><a href="#_9-vue-子组件和父组件执行顺序" class="header-anchor">#</a> <strong>9. Vue 子组件和父组件执行顺序</strong></h2> <ul><li>加载渲染过程：
<ul><li>父组件 beforeCreate</li> <li>父组件 created</li> <li>父组件 beforeMount</li> <li>子组件 beforeCreate</li> <li>子组件 created</li> <li>子组件 beforeMount</li> <li>子组件 mounted</li> <li>父组件 mounted</li></ul></li> <li>更新过程：
<ul><li>父组件 beforeUpdate</li> <li>子组件 beforeUpdate</li> <li>子组件 updated</li> <li>父组件 updated</li></ul></li> <li>销毁过程：
<ul><li>父组件 beforeDestroy</li> <li>子组件 beforeDestroy</li> <li>子组件 destroyed</li> <li>父组件 destoryed</li></ul></li></ul> <h2 id="_10-一般在哪个生命周期请求异步数据"><a href="#_10-一般在哪个生命周期请求异步数据" class="header-anchor">#</a> <strong>10. 一般在哪个生命周期请求异步数据</strong></h2> <ul><li>我们可以在钩子函数 created、beforeMount、mounted 中进行调用，因为在这三个钩子函数中，data 已经创建，可以将服务端端返回的数据进行赋值。</li> <li>推荐在 created 钩子函数中调用异步请求，因为在 created 钩子函数中调用异步请求有以下优点：
<ul><li>能更快获取到服务端数据，减少页面加载时间，用户体验更好；</li> <li>SSR不支持 beforeMount 、mounted 钩子函数，放在 created 中有助于一致性。</li></ul></li></ul> <h2 id="_11-vue-组件通信方式"><a href="#_11-vue-组件通信方式" class="header-anchor">#</a> <strong>11. Vue 组件通信方式</strong></h2> <p>（1）父子组件间通信</p> <ul><li>子组件通过 props 属性来接受父组件的数据，然后父组件在子组件上注册监听事件，子组件通过 <code>$emit</code> 触发事件来向父组件发送数据。</li> <li>通过 ref 属性给子组件设置一个名字。父组件通过 <code>$refs</code> 组件名来获得子组件，子组件通过 <code>$parent</code> 获得父组件，这样也可以实现通信。</li> <li>使用 provide/inject，在父组件中通过 provide提供变量，在子组件中通过 inject 来将变量注入到组件中。不论子组件有多深，只要调用了 inject 那么就可以注入 provide中的数据。</li></ul> <p>（2）兄弟组件间通信</p> <ul><li>使用 eventBus 的方法，它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象，通信的组件引入这个实例，通信的组件通过在这个实例上监听和触发事件，来实现消息的传递。</li> <li>通过 <code>$parent/$refs</code> 来获取到兄弟组件，也可以进行通信。</li></ul> <p>（3）任意组件之间</p> <ul><li>使用 eventBus ，其实就是创建一个事件中心，相当于中转站，可以用它来传递事件和接收事件。</li> <li>Vuex：如果业务逻辑复杂，很多组件之间需要同时处理一些公共的数据，这个时候采用上面这一些方法可能不利于项目的维护。这个时候可以使用vuex，vuex 的思想就是将这一些公共的数据抽离出来，将它作为一个全局的变量来管理，然后其他组件就可以对这个公共数据进行读写操作，这样达到了解耦的目的。</li></ul> <h2 id="_12-vue-路由模式有哪些-有什么区别"><a href="#_12-vue-路由模式有哪些-有什么区别" class="header-anchor">#</a> <strong>12. Vue 路由模式有哪些？有什么区别？</strong></h2> <ul><li>vue的路由模式⼀共有两种,分别是hash和history</li> <li>区别
<ul><li>它们的区别是 hash 模式不会包含在 http 请求当中,并且 hash 不会重新加载⻚⾯</li> <li>使⽤history模式的话,如果前端的url和后端发起请求的url不⼀致的话,会 报404错误,所以使⽤history模块的话我们需要和后端进⾏配合.</li></ul></li> <li>原理
<ul><li>history的原理就是利⽤html5新增的两个特性⽅法,分别是psuhState和replaceState来完成的，这就是我对vue路由模式的理解</li></ul></li></ul> <h2 id="_13-说说-vue-router-的-hash模式"><a href="#_13-说说-vue-router-的-hash模式" class="header-anchor">#</a> <strong>13. 说说 Vue Router 的 hash模式</strong></h2> <ul><li>简介： hash模式是开发中默认的模式，它的URL带着一个#，例如：<code>www.abc.com/#/vue</code>，它的hash值就是<code>#/vue</code>。</li> <li>特点：hash值会出现在 URL 里面，但是不会出现在 HTTP 请求中，对后端完全没有影响。所以改变 hash 值，不会重新加载页面。这种模式的浏览器支持度很好，低版本的IE浏览器也支持这种模式。hash路由被称为是前端路由，已经成为SPA（单页面应用）的标配。</li> <li>原理： hash 模式的主要原理就是 onhashchange() 事件。</li> <li>使用 onhashchange() 事件的好处就是，在页面的 hash 值发生变化时，无需向后端发起请求，window就可以监听事件的改变，并按规则加载相应的代码。除此之外，hash值变化对应的URL都会被浏览器记录下来，这样浏览器就能实现页面的前进和后退。虽然是没有请求后端服务器，但是页面的hash值和对应的URL关联起来了。</li></ul> <h2 id="_14-route-和-router-的区别"><a href="#_14-route-和-router-的区别" class="header-anchor">#</a> <strong>14. <code>$route</code> 和<code>$router</code> 的区别</strong></h2> <ul><li><code>$route</code> 是“路由信息对象”，包括 path，params，hash，query，fullPath，matched，name 等路由信息参数</li> <li><code>$router</code> 是“路由实例”对象包括了路由的跳转方法，钩子函数等。</li></ul> <h2 id="_15-vuex-是什么-核心流程"><a href="#_15-vuex-是什么-核心流程" class="header-anchor">#</a> <strong>15. Vuex 是什么？核心流程？</strong></h2> <ul><li><p>Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store（仓库）。“store” 基本上就是一个容器，它包含着你的应用中大部分的状态 ( state )。</p> <ul><li>Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候，若 store 中的状态发生变化，那么相应的组件也会相应地得到高效更新。</li> <li>改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样可以方便地跟踪每一个状态的变化。</li></ul></li> <li><p>核心流程中的主要功能：
<img src="/blog/img/frame_vuex.jpg"></p></li> <li><p>Vue Components 是 vue 组件，组件会触发（dispatch）一些事件或动作，也就是图中的 Actions;</p></li> <li><p>在组件中发出的动作，肯定是想获取或者改变数据的，但是在 vuex 中，数据是集中管理的，不能直接去更改数据，所以会把这个动作提交（Commit）到 Mutations 中;</p></li> <li><p>然后 Mutations 就去改变（Mutate）State 中的数据;</p></li> <li><p>当 State 中的数据被改变之后，就会重新渲染（Render）到 Vue Components 中去，组件展示更新后的数据，完成一个流程。</p></li></ul> <h2 id="_16-vuex中action和mutation的区别"><a href="#_16-vuex中action和mutation的区别" class="header-anchor">#</a> <strong>16. Vuex中action和mutation的区别</strong></h2> <ul><li>Mutation专注于修改State，理论上是修改State的唯一途径；Action业务代码、异步请求。</li> <li>Mutation：必须同步执行；Action：可以异步，但不能直接操作State。</li> <li>在视图更新时，先触发actions，actions再触发mutation</li> <li>mutation的参数是state，它包含store中的数据；store的参数是context，它是 state 的父级，包含 state、getters</li></ul> <h2 id="_17-redux-和-vuex-有什么区别-它们的共同思想"><a href="#_17-redux-和-vuex-有什么区别-它们的共同思想" class="header-anchor">#</a> <strong>17. Redux 和 Vuex 有什么区别，它们的共同思想</strong></h2> <p>（1）Redux 和 Vuex区别</p> <ul><li>Vuex改进了Redux中的Action和Reducer函数，以mutations变化函数取代Reducer，无需switch，只需在对应的mutation函数里改变state值即可</li> <li>Vuex由于Vue自动重新渲染的特性，无需订阅重新渲染函数，只要生成新的State即可</li> <li>Vuex数据流的顺序是∶View调用store.commit提交对应的请求到Store中对应的mutation函数-&gt;store改变（vue检测到数据变化自动渲染）</li></ul> <p>（2）共同思想</p> <ul><li>单—的数据源</li> <li>变化可以预测</li> <li>本质上：redux与vuex都是对mvvm思想的服务，将数据从视图中抽离的一种方案; 形式上：vuex借鉴了redux，将store作为全局的数据中心，进行mode管理;</li></ul> <h2 id="_18-vuex-和-localstorage-的区别"><a href="#_18-vuex-和-localstorage-的区别" class="header-anchor">#</a> <strong>18. Vuex 和 localStorage 的区别</strong></h2> <p>（1）最重要的区别</p> <ul><li>vuex存储在内存中</li> <li>localstorage 则以文件的方式存储在本地，只能存储字符串类型的数据，存储对象需要 JSON的stringify和parse方法进行处理。 读取内存比读取硬盘速度要快</li></ul> <p>（2）应用场景</p> <ul><li>Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态，并以相应的规则保证状态以一种可预测的方式发生变化。vuex用于组件之间的传值。</li> <li>localstorage是本地存储，是将数据存储到浏览器的方法，一般是在跨页面传递数据时使用 。</li> <li>Vuex能做到数据的响应式，localstorage不能</li></ul> <p>（3）永久性</p> <ul><li>刷新页面时vuex存储的值会丢失，localstorage不会。</li> <li>注意： 对于不变的数据确实可以用localstorage可以代替vuex，但是当两个组件共用一个数据源（对象或数组）时，如果其中一个组件改变了该数据源，希望另一个组件响应该变化时，localstorage无法做到，原因就是区别1。</li></ul> <h2 id="_19-vue3-0有什么更新"><a href="#_19-vue3-0有什么更新" class="header-anchor">#</a> <strong>19. Vue3.0有什么更新</strong></h2> <p>（1）监测机制的改变</p> <ul><li>3.0 将带来基于代理 Proxy的 observer 实现，提供全语言覆盖的反应性跟踪。</li> <li>消除了 Vue 2 当中基于 Object.defineProperty 的实现所存在的很多限制：</li></ul> <p>（2）只能监测属性，不能监测对象</p> <ul><li>检测属性的添加和删除；</li> <li>检测数组索引和长度的变更；</li> <li>支持 Map、Set、WeakMap 和 WeakSet。</li></ul> <p>（3）模板</p> <ul><li>作用域插槽，2.x 的机制导致作用域插槽变了，父组件会重新渲染，而 3.0 把作用域插槽改成了函数的方式，这样只会影响子组件的重新渲染，提升了渲染的性能。</li> <li>同时，对于 render 函数的方面，vue3.0 也会进行一系列更改来方便习惯直接使用 api 来生成 vdom 。</li></ul> <p>（4）对象式的组件声明方式</p> <ul><li>vue2.x 中的组件是通过声明的方式传入一系列 option，和 TypeScript 的结合需要通过一些装饰器的方式来做，虽然能实现功能，但是比较麻烦。</li> <li>3.0 修改了组件的声明方式，改成了类式的写法，这样使得和 TypeScript 的结合变得很容易</li></ul> <p>（5）其它方面的更改</p> <ul><li>支持自定义渲染器，从而使得 weex 可以通过自定义渲染器的方式来扩展，而不是直接 fork 源码来改的方式。</li> <li>支持 Fragment（多个根节点）和 Protal（在 dom 其他部分渲染组建内容）组件，针对一些特殊的场景做了处理。</li> <li>基于 tree shaking 优化，提供了更多的内置功能。</li></ul> <h2 id="_20-defineproperty和proxy的区别"><a href="#_20-defineproperty和proxy的区别" class="header-anchor">#</a> <strong>20. defineProperty和proxy的区别</strong></h2> <ul><li>Vue 在实例初始化时遍历 data 中的所有属性，并使用 Object.defineProperty 把这些属性全部转为 getter/setter。这样当追踪数据发生变化时，setter 会被自动调用。</li> <li>Object.defineProperty 是 ES5 中一个无法 shim 的特性，这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。</li> <li>但是这样做有以下问题：
<ul><li>添加或删除对象的属性时，Vue 检测不到。因为添加或删除的对象没有在初始化进行响应式处理，只能通过<code>$set</code> 来调用Object.defineProperty()处理。</li> <li>无法监控到数组下标和长度的变化。</li></ul></li> <li>Vue3 使用 Proxy 来监控数据的变化。Proxy 是 ES6 中提供的功能，其作用为：用于定义基本操作的自定义行为（如属性查找，赋值，枚举，函数调用等）。相对于Object.defineProperty()，其有以下特点：
<ul><li>Proxy 直接代理整个对象而非对象属性，这样只需做一层代理就可以监听同级结构下的所有属性变化，包括新增属性和删除属性。</li> <li>Proxy 可以监听数组的变化。</li></ul></li></ul> <h2 id="_21-对虚拟dom的理解"><a href="#_21-对虚拟dom的理解" class="header-anchor">#</a> <strong>21. 对虚拟DOM的理解？</strong></h2> <ul><li>从本质上来说，Virtual Dom是一个JavaScript对象，通过对象的方式来表示DOM结构。将页面的状态抽象为JS对象的形式，配合不同的渲染工具，使跨平台渲染成为可能。通过事务处理机制，将多次DOM修改的结果一次性的更新到页面上，从而有效的减少页面渲染的次数，减少修改DOM的重绘重排次数，提高渲染性能。</li> <li>虚拟DOM是对DOM的抽象，这个对象是更加轻量级的对 DOM的描述。它设计的最初目的，就是更好的跨平台，比如Node.js就没有DOM，如果想实现SSR，那么一个方式就是借助虚拟DOM，因为虚拟DOM本身是js对象。 在代码渲染到页面之前，vue会把代码转换成一个对象（虚拟 DOM）。以对象的形式来描述真实DOM结构，最终渲染到页面。在每次数据发生变化前，虚拟DOM都会缓存一份，变化之时，现在的虚拟DOM会与缓存的虚拟DOM进行比较。在vue内部封装了diff算法，通过这个算法来进行比较，渲染时修改改变的变化，原先没有发生改变的通过原先的数据进行渲染。</li> <li>另外现代前端框架的一个基本要求就是无须手动操作DOM，一方面是因为手动操作DOM无法保证程序性能，多人协作的项目中如果review不严格，可能会有开发者写出性能较低的代码，另一方面更重要的是省略手动DOM操作可以大大提高开发效率。</li></ul> <h2 id="_22-虚拟dom的解析过程"><a href="#_22-虚拟dom的解析过程" class="header-anchor">#</a> <strong>22. 虚拟DOM的解析过程</strong></h2> <ul><li>首先对将要插入到文档中的 DOM 树结构进行分析，使用 js 对象将其表示出来，比如一个元素对象，包含 TagName、props 和 Children 这些属性。然后将这个 js 对象树给保存下来，最后再将 DOM 片段插入到文档中。</li> <li>当页面的状态发生改变，需要对页面的 DOM 的结构进行调整的时候，首先根据变更的状态，重新构建起一棵对象树，然后将这棵新的对象树和旧的对象树进行比较，记录下两棵树的的差异。</li> <li>最后将记录的有差异的地方应用到真正的 DOM 树中去，这样视图就更新了。</li></ul> <h2 id="_23-双向数据绑定的原理"><a href="#_23-双向数据绑定的原理" class="header-anchor">#</a> <strong>23. 双向数据绑定的原理</strong></h2> <ul><li>Vue.js 是采用数据劫持结合发布者-订阅者模式的方式，通过Object.defineProperty()来劫持各个属性的setter，getter，在数据变动时发布消息给订阅者，触发相应的监听回调。主要分为以下几个步骤：</li> <li>需要observe的数据对象进行递归遍历，包括子属性对象的属性，都加上setter和getter这样的话，给这个对象的某个值赋值，就会触发setter，那么就能监听到了数据变化</li> <li>compile解析模板指令，将模板中的变量替换成数据，然后初始化渲染页面视图，并将每个指令对应的节点绑定更新函数，添加监听数据的订阅者，一旦数据有变动，收到通知，更新视图</li> <li>Watcher订阅者是Observer和Compile之间通信的桥梁，主要做的事情是:
<ul><li>①在自身实例化时往属性订阅器(dep)里面添加自己</li> <li>②自身必须有一个update()方法</li> <li>③待属性变动dep.notice()通知时，能调用自身的update()方法，并触发Compile中绑定的回调，则功成身退。</li></ul></li> <li>MVVM作为数据绑定的入口，整合Observer、Compile和Watcher三者，通过Observer来监听自己的model数据变化，通过Compile来解析编译模板指令，最终利用Watcher搭起Observer和Compile之间的通信桥梁，达到数据变化 -&gt; 视图更新；视图交互变化(input) -&gt; 数据model变更的双向绑定效果。
<img src="/blog/img/frame_mvvm.jpeg"></li></ul> <h2 id="_24-diff-算法"><a href="#_24-diff-算法" class="header-anchor">#</a> <strong>24. Diff 算法</strong></h2> <ul><li>在新老虚拟DOM对比时：</li> <li>首先，对比节点本身
<ul><li>没有新节点，直接触发旧节点的destory钩子</li> <li>没有旧节点，说明是页面刚开始初始化的时候，此时，根本不需要比较了，直接全是新建，所以只调用 createElm</li> <li>旧节点和新节点自身一样，通过 sameVnode 判断节点是否一样，一样时，直接调用 patchVnode 去处理这两个节点</li> <li>旧节点和新节点自身不一样，当两个节点不一样的时候，直接创建新节点，删除旧节点</li></ul></li> <li>如果为相同节点，进行patchVnode，判断如何对该节点的子节点进行处理，先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点，将旧的子节点移除)
<ul><li>新节点是否是文本节点，如果是，则直接更新dom的文本内容为新节点的文本内容</li> <li>新节点和旧节点如果都有子节点，则处理比较更新子节点</li> <li>只有新节点有子节点，旧节点没有，那么不用比较了，所有节点都是全新的，所以直接全部新建就好了，新建是指创建出所有新DOM，并且添加进父节点</li> <li>只有旧节点有子节点而新节点没有，说明更新后的页面，旧节点全部都不见了，那么要做的，就是把所有的旧节点删除，也就是直接把DOM 删除</li></ul></li> <li>比较如果都有子节点，则进行updateChildren，判断如何对这些新老节点的子节点进行操作（diff核心）。
<ul><li>设置新旧VNode的头尾指针</li> <li>新旧头尾指针进行比较，循环向中间靠拢，根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点，从哈希表寻找 key一致的VNode 节点再分情况操作</li></ul></li> <li>匹配时，找到相同的子节点，递归比较子节点</li> <li>在diff中，只对同层的子节点进行比较，放弃跨级的节点比较，使得时间复杂从O(n3)降低值O(n)，也就是说，只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。</li></ul> <h2 id="_25-vue-为什么要设置-key"><a href="#_25-vue-为什么要设置-key" class="header-anchor">#</a> <strong>25. Vue 为什么要设置 key？</strong></h2> <ul><li><p>vue 中 key 值的作用可以分为两种情况来考虑：</p> <ul><li>第一种情况是 v-if 中使用 key。由于 Vue 会尽可能高效地渲染元素，通常会复用已有元素而不是从头开始渲染。因此当使用 v-if 来实现元素切换的时候，如果切换前后含有相同类型的元素，那么这个元素就会被复用。如果是相同的 input 元素，那么切换前后用户的输入不会被清除掉，这样是不符合需求的。因此可以通过使用 key 来唯一的标识一个元素，这个情况下，使用 key 的元素不会被复用。这个时候 key 的作用是用来标识一个独立的元素。</li> <li>第二种情况是 v-for 中使用 key。用 v-for 更新已渲染过的元素列表时，它默认使用“就地复用”的策略。如果数据项的顺序发生了改变，Vue 不会移动 DOM 元素来匹配数据项的顺序，而是简单复用此处的每个元素。因此通过为每个列表项提供一个 key 值，来以便 Vue 跟踪元素的身份，从而高效的实现复用。这个时候 key 的作用是为了高效的更新渲染虚拟 DOM。</li></ul></li> <li><p>key 是为 Vue 中 vnode 的唯一标记，通过这个 key，diff 操作可以更准确、更快速</p> <ul><li>更准确：因为带 key 就不是就地复用了，在 sameNode 函数a.key === b.key对比中可以避免就地复用的情况。所以会更加准确。</li> <li>更快速：利用 key 的唯一性生成 map 对象来获取对应节点，比遍历方式更快</li></ul></li></ul> <h2 id="_26-为什么不建议用index作为key"><a href="#_26-为什么不建议用index作为key" class="header-anchor">#</a> <strong>26. 为什么不建议用index作为key?</strong></h2> <ul><li>使用index 作为 key和没写基本上没区别，因为不管数组的顺序怎么颠倒，index 都是 0, 1, 2...这样排列，导致 Vue 会复用错误的旧子节点，做很多额外的工作。</li></ul> <h2 id="_27-编译的过程"><a href="#_27-编译的过程" class="header-anchor">#</a> <strong>27. 编译的过程</strong></h2> <ul><li>将模板解析为 AST
<ul><li>最主要的事情还是通过各种各样的正则表达式去匹配模板中的内容，然后将内容提取出来做各种逻辑操作，接下来会生成一个最基本的 AST 对象</li></ul></li> <li>优化 AST
<ul><li>对静态节点做优化</li></ul></li> <li>将 AST 转换为 render 函数</li></ul> <h2 id="_28-nexttick-原理分析"><a href="#_28-nexttick-原理分析" class="header-anchor">#</a> <strong>28. NextTick 原理分析</strong></h2> <ul><li>对于实现 macrotasks ，会先判断是否能使用 setImmediate ，不能的话降级为 MessageChannel ，以上都不行的话就使用 setTimeout</li> <li>Vue 的 nextTick 其本质是对 JavaScript 执行原理 EventLoop 的一种应用。</li> <li>nextTick 的核心是利用了如 Promise 、MutationObserver、setImmediate、setTimeout的原生 JavaScript 方法来模拟对应的微/宏任务的实现，本质是为了利用 JavaScript 的这些异步回调任务队列来实现 Vue 框架中自己的异步回调队列。</li> <li>nextTick 不仅是 Vue 内部的异步队列的调用方法，同时也允许开发者在实际项目中使用这个方法来满足实际应用中对 DOM 更新数据时机的后续逻辑处理</li> <li>问题
<ul><li>如果是同步更新，则多次对一个或多个属性赋值，会频繁触发 UI/DOM 的渲染，可以减少一些无用渲染</li> <li>同时由于 VirtualDOM 的引入，每一次状态发生变化后，状态变化的信号会发送给组件，组件内部使用 VirtualDOM 进行计算得出需要更新的具体的 DOM 节点，然后对 DOM 进行更新操作，每次更新状态后的渲染过程需要更多的计算，而这种无用功也将浪费更多的性能，所以异步渲染变得更加至关重要</li></ul></li></ul></div> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><span class="prev">
      ←
      <a href="/blog/interview/js/22.html" class="prev">
        22. for of, for in和forEach, map的区别？
      </a></span> <span class="next"><a href="/blog/interview/frame/2.html">
        2. 30道vue面试题
      </a>
      →
    </span></p></div> </main></div><div class="global-ui"></div></div>
    <script src="/blog/assets/js/app.43c5da00.js" defer></script><script src="/blog/assets/js/2.d3a12b6d.js" defer></script><script src="/blog/assets/js/1.f8bb34da.js" defer></script><script src="/blog/assets/js/40.dfcfa312.js" defer></script>
  </body>
</html>
